package io.lvdaxian.mybatis17.scripting.xmltags;

import io.lvdaxian.mybatis17.session.Configuration;

import java.util.*;

/**
 * trim sql node 节点
 *
 * @author lihh
 */
public class TrimSqlNode implements SqlNode {
  
  // sql node 的内容
  private SqlNode contents;
  // 前缀
  private String prefix;
  // 后缀
  private String suffix;
  // todo 前缀覆盖 后端覆盖
  private List<String> prefixesToOverride;
  private List<String> suffixesToOverride;
  // 表示 配置信息
  private Configuration configuration;
  
  public TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, String prefixesToOverride, String suffix, String suffixesToOverride) {
    this(configuration, contents, prefix, parseOverrides(prefixesToOverride), suffix, parseOverrides(suffixesToOverride));
  }
  
  protected TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, List<String> prefixesToOverride, String suffix, List<String> suffixesToOverride) {
    this.contents = contents;
    this.prefix = prefix;
    this.prefixesToOverride = prefixesToOverride;
    this.suffix = suffix;
    this.suffixesToOverride = suffixesToOverride;
    this.configuration = configuration;
  }
  
  @Override
  public boolean apply(DynamicContext context) {
    FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
    boolean result = contents.apply(filteredDynamicContext);
    filteredDynamicContext.applyAll();
    return result;
  }
  
  private static List<String> parseOverrides(String overrides) {
    if (overrides != null) {
      final StringTokenizer parser = new StringTokenizer(overrides, "|", false);
      final List<String> list = new ArrayList<String>(parser.countTokens());
      while (parser.hasMoreTokens()) {
        list.add(parser.nextToken().toUpperCase(Locale.ENGLISH));
      }
      return list;
    }
    return Collections.emptyList();
  }
  
  // 过滤的 动态上下文
  private class FilteredDynamicContext extends DynamicContext {
    // 动态上下文
    private DynamicContext delegate;
    // 前缀
    private boolean prefixApplied;
    // 后缀
    private boolean suffixApplied;
    // sql buffer string
    private StringBuilder sqlBuffer;
    
    public FilteredDynamicContext(DynamicContext delegate) {
      super(configuration, null);
      this.delegate = delegate;
      this.prefixApplied = false;
      this.suffixApplied = false;
      this.sqlBuffer = new StringBuilder();
    }
    
    public void applyAll() {
      sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());
      String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);
      if (!trimmedUppercaseSql.isEmpty()) {
        applyPrefix(sqlBuffer, trimmedUppercaseSql);
        applySuffix(sqlBuffer, trimmedUppercaseSql);
      }
      delegate.appendSql(sqlBuffer.toString());
    }
    
    @Override
    public Map<String, Object> getBindings() {
      return delegate.getBindings();
    }
    
    @Override
    public void bind(String name, Object value) {
      delegate.bind(name, value);
    }
    
    @Override
    public int getUniqueNumber() {
      return delegate.getUniqueNumber();
    }
    
    @Override
    public void appendSql(String sql) {
      sqlBuffer.append(sql);
    }
    
    @Override
    public String getSql() {
      return delegate.getSql();
    }
    
    /**
     * 应用前缀
     *
     * @param sql                 sql buffer
     * @param trimmedUppercaseSql 这是trim 掉的字符串
     * @author lihh
     */
    private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
      // 尝试前缀处理
      if (!prefixApplied) {
        prefixApplied = true;
        if (prefixesToOverride != null) {
          for (String toRemove : prefixesToOverride) {
            if (trimmedUppercaseSql.startsWith(toRemove)) {
              sql.delete(0, toRemove.trim().length());
              break;
            }
          }
        }
        if (prefix != null) {
          sql.insert(0, " ");
          sql.insert(0, prefix);
        }
      }
    }
    
    private void applySuffix(StringBuilder sql, String trimmedUppercaseSql) {
      if (!suffixApplied) {
        suffixApplied = true;
        if (suffixesToOverride != null) {
          for (String toRemove : suffixesToOverride) {
            if (trimmedUppercaseSql.endsWith(toRemove) || trimmedUppercaseSql.endsWith(toRemove.trim())) {
              int start = sql.length() - toRemove.trim().length();
              int end = sql.length();
              sql.delete(start, end);
              break;
            }
          }
        }
        if (suffix != null) {
          sql.append(" ");
          sql.append(suffix);
        }
      }
    }
    
  }
  
}
